package com.ibm.rules;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import org.apache.log4j.Logger;

import com.google.common.base.Throwables;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.ibm.cps.message.ParsedDataMessage;
import com.ibm.cps.newmessage.RuleMetaData;
import com.ibm.interfaces.IRuleEngine;
import com.ibm.interfaces.ITimeSeriesSchema;
import com.ibm.rules.tree.RuleTreeNode;
import com.ibm.util.ErrorCode;
import com.ibm.util.exception.CPSException;
// TODO
// ruleMap.putAll(tmpMap);
// ruleMap.putAll(ruleName, Lists.newArrayList(rules));
/* TODO P1 rebuild the tree when necessary */
// root.deleteRuleByName(ruleName);
// TODO
// ruleMap.putAll(tmpMap);
// ruleMap.putAll(ruleName, Lists.newArrayList(rules));
/* TODO P1 rebuild the tree when necessary */

// root.deleteRuleByName(ruleName);
public class RuleEngine implements IRuleEngine {
	private static Logger logger = Logger.getLogger(RuleEngine.class);
	private RuleTreeNode _root = null;

	// TODO
	private Collection<String> removedRules;

	public RuleEngine() {
		removedRules = new HashSet<String>();
	}

	@Override
	public Collection<BigInteger> executeRules(ParsedDataMessage message)
			throws CPSException {
		if (message == null) {
			logger.error("Null parsed data message");
		}

//		Collection<BigInteger> hitRules = _root.executeAllRules(message
//				.getValues());
		Collection<BigInteger> hitRules = _root.executeAllRules(message);

		BigInteger removedRule;
		for (String removed : removedRules) {
			removedRule = new BigInteger(removed, 16);
			if (hitRules.contains(removedRule)) {
				hitRules.remove(removedRule);
			}
		}
		return hitRules;
	}

	public Collection<BigInteger> executeRules(Comparable[] fact)
			throws CPSException {

		RuleTreeNode root = _root;
		if (root != null) {
			return root.executeAllRules(fact);
		}
		return new ArrayList<BigInteger>();
	}

	@Override
	public void addRules(ITimeSeriesSchema schema,
			Collection<RuleMetaData> ruleRecs) throws CPSException {

		if (schema == null) {
			throw new CPSException(ErrorCode.KEY_NOT_EXIST_CODE,
					"schema does not exist");
		}

		RuleTreeNode root = _root;
		if (root != null) {
			for (RuleMetaData ruleRec : ruleRecs) {
				addRule(schema, ruleRec.getProcessorid(),
						ruleRec.getRuleString());
			}
			return;
		}

		try {

			List<Rule> rules = new ArrayList<Rule>();
			ArrayListMultimap<String, Rule> tmpMap = ArrayListMultimap
					.<String, Rule> create();
			for (RuleMetaData ruleRec : ruleRecs) {
				BigInteger ruleId = new BigInteger(ruleRec.getProcessorid(), 16);
				Rule[] now = RuleParser.parseRule(schema, ruleId,
						ruleRec.getRuleString());
				rules.addAll(Lists.newArrayList(now));
				tmpMap.putAll(ruleRec.getName(), Lists.newArrayList(now));
			}
			_root = RuleTreeNode.feed(schema,
					rules.toArray(new Rule[rules.size()]));
			// ruleMap.putAll(tmpMap);
		} catch (CPSException e) {
			throw e;
		} catch (Exception e) {
			logger.error(e.getClass() + " - " + e.getMessage());
			logger.error(Throwables.getStackTraceAsString(e));
			throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
					e.getMessage());
		}
	}

	@Override
	public void addRule(ITimeSeriesSchema schema, String ruleIdString,
			String ruleString) throws CPSException {

		if (removedRules.contains(ruleIdString)) {
			removedRules.remove(ruleIdString);
		}

		if (schema == null) {
			throw new CPSException(ErrorCode.KEY_NOT_EXIST_CODE,
					"schema does not exist");
		}
		try {
			BigInteger ruleId = new BigInteger(ruleIdString, 16);
			Rule[] rules = RuleParser.parseRule(schema, ruleId, ruleString);
			RuleTreeNode root = _root;
			if (root == null) {
				_root = RuleTreeNode.feed(schema, rules);
			} else {
				for (int i = 0; i < rules.length; i++) {
					root.addIncrementalRule(rules[i]);
				}
			}
			// ruleMap.putAll(ruleName, Lists.newArrayList(rules));
			/* TODO P1 rebuild the tree when necessary */

		} catch (CPSException e) {
			throw e;
		} catch (Exception e) {
			if (e instanceof NumberFormatException) {
				throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
						"Invalid id " + ruleIdString);
			}
			logger.error(e.getClass() + " - " + e.getMessage());
			logger.error(Throwables.getStackTraceAsString(e));

			throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
					e.getMessage());
		}
	}

	@Override
	public void removeRule(ITimeSeriesSchema schema, String ruleid)
			throws CPSException {
		RuleTreeNode root = _root;
		if (root == null)
			throw new CPSException(ErrorCode.KEY_NOT_EXIST_CODE,
					"rule does not exist");
		// root.deleteRuleByName(ruleName);
		removedRules.add(ruleid);
	}

	public static void main(String[] args) {
		String id = "1888c8f0c97a4ee-495b05c2b60398d41";
//		BigInteger ruleId = new BigInteger(id, 16);
	}
}
